
/**********************************************************************
*file:基于内存实现简易版文件存储
*author:残梦
*versions:V1.0
*date:2025.7.26
*note:
    本文件是直接基于内存实现的文件储存，避免耗费较大SRAM,程序内无静态大数组开销内存
    添加新文件方法:
        Memory_FileObject_EnumDef//注册文件对象
        #define dMemory_FileName(object)//添加文件对应名称
        Memory_FileFormatContentSize_Get()//添加文件内容行数据字节大小
        Memory_FileSize_Get()//规定文件最大字节数
        Memory_FileDataPrintf()//添加文件打印内容

    使用方法:
        uint32_t Memory_FileSize_Get(Memory_FileObject_EnumDef object);//获取文件最大字节数(也可以获取所有文件大小,不含对齐扇区损耗)
        uint32_t Memory_FileAddress_Get(Memory_FileObject_EnumDef object);//获取文件起始地址(也可以获取文件管理结束地址,含对齐扇区损耗)
        uint16_t Memory_FileLineNumber_Read(Memory_FileObject_EnumDef object);//读取文件数据行数
        void Memory_FileLineNumber_Write(Memory_FileObject_EnumDef object,uint16_t number);//写入文件数据行数
        bool Memory_FileContent_Read(Memory_FileObject_EnumDef object,uint16_t index,void *content,uint16_t number);//文件数据读取
        void Memory_FileContent_Write(Memory_FileObject_EnumDef object,uint16_t index,void *content,uint16_t number);//文件数据写入
        void Memory_FileDataPrintf(Memory_FileObject_EnumDef object);//文件打印

    内存规则:
        文件存储区域起始地址:dMemory_FlashStartAddress
        文件存储区域结束地址:Memory_FileAddress_Get(eMemory_FileObject_Num)
        存储最小扇区:dMemory_FlashSmallestUnit;当此值为1时,文件管理占用内存=所有文件大小求和;通过此查看(dMemory_LOG("AllFileSize:%d AddressEnd:%d\n",Memory_FileSize_Get(eMemory_FileObject_Num),Memory_FileAddress_Get(eMemory_FileObject_Num));)
        文件存储顺序见:Memory_FileObject_EnumDef

    文件管理方式如下:
        文件行数:n:格式见FileFormatLineNumber_StructDef
        行数据:0:格式见FileFormatContent_XXX_StructDef,文件行数据字节大小见Memory_FileFormatContentSize_Get()函数
        行数据:1
        ...
        行数据:n-1
        文件分配最大字节数见Memory_FileSize_Get()
    文件使用内存数据读写接口:
        #include "w25qxxx_driver.h"
        W25QXXX_ReadData()
        W25QXXX_WriteData()
    
    示例代码:
        Memory_FileObject_EnumDef object = eMemory_FileObject_SystemConfiguration;
        FileFormatContent_SystemConfiguration_StructDef config = {.address = 0x00};

        if(Memory_FileLineNumber_Read(object) == 0)
        {
            config.address = 10;
            Memory_FileContent_Write(object,0,(void *)(&config),1);
            dData_LOG("config data write...\n");
        }
        Memory_FileDataPrintf(object);

        FileFormatContent_User_StructDef data[2];
        object = eMemory_FileObject_User0;
        if(Memory_FileLineNumber_Read(object) == 0)
        {
            Memory_FileLineNumber_Write(object,2);
            data[0].SerialNumber = 0;
            data[0].temperature = 20.3f;
            data[0].concentration = 112.0f;
            data[0].VoltageTemperature = 2.2f;
            data[0].VoltageHydrogen = 1.23f;

            data[1].SerialNumber = 1;
            data[1].temperature = 30.3f;
            data[1].concentration = 132.0f;
            data[1].VoltageTemperature = 4.2f;
            data[1].VoltageHydrogen = 5.23f;

            Memory_FileContent_Write(object,0,(void *)data,2);
            dData_LOG("User0 data write...\n");
        }
        Memory_FileDataPrintf(object);
        dData_LOG("AllFileSize:%d AddressEnd:%d\n",Memory_FileSize_Get(eMemory_FileObject_Num),Memory_FileAddress_Get(eMemory_FileObject_Num));
        dData_LOG("test finish\n");
**********************************************************************/
#include "memory.h"
#include "w25qxxx_driver.h"

#define dVerifyIdentifyA 0x81
#define dVerifyIdentifyB 0x7E

#define dMemory_FlashStartAddress 0//存储区域起始地址
#define dMemory_FlashSmallestUnit 4096//存储最小扇区,单位字节;对文件大小进行扇区对齐,可以使用flash扇区擦除最小单元,避免写文件反复擦除flash

typedef struct
{
    uint8_t identifyA;//校验标识符:dVerifyIdentifyA
    uint8_t identifyB;//校验标识符:dVerifyIdentifyB
    uint16_t value;//值
}FileFormatLineNumber_StructDef;//文件行数格式

/****************************************************
@function:获取文件内容格式字节数
@param:object:对象
@return:字节数
@note:
****************************************************/
uint32_t Memory_FileFormatContentSize_Get(Memory_FileObject_EnumDef object)
{
    uint32_t size = 0;

    switch(object)
    {
        case eMemory_FileObject_SystemConfiguration:size = sizeof(FileFormatContent_SystemConfiguration_StructDef);break;
        case eMemory_FileObject_User0:size = sizeof(FileFormatContent_User_StructDef);break;
        case eMemory_FileObject_VoltageTemperature:
        case eMemory_FileObject_VoltageHydrogen:size = sizeof(FileFormatContent_CalibrationData_StructDef);break;
        default:return 0;
    }
    return size;
}

/****************************************************
@function:获取文件固定分配占用字节数
@param:object:对象,等于eMemory_FileObject_Num则表示读取所有文件占用大小
@return:字节数
@note:此大小必须评估需要消耗的文件最大容量，禁止使用Memory_FileLineNumber_Read()动态变化，会陷入死循环
****************************************************/
uint32_t Memory_FileSize_Get(Memory_FileObject_EnumDef object)
{
    uint32_t size = sizeof(FileFormatLineNumber_StructDef);

    switch(object)
    {
        case eMemory_FileObject_SystemConfiguration:
        {
            size += sizeof(FileFormatContent_SystemConfiguration_StructDef);
        }break;
        case eMemory_FileObject_User0:
        {
            size += sizeof(FileFormatContent_User_StructDef) * dMemory_FileLineNumberMax_User0;
        }break;
        case eMemory_FileObject_VoltageTemperature:
        case eMemory_FileObject_VoltageHydrogen:
        {
            size += sizeof(FileFormatContent_CalibrationData_StructDef) * dMemory_FileLineNumberMax_User0;
        }break;
        case eMemory_FileObject_Num:
        {
            size = 0;
            for(uint16_t i = 0;i < eMemory_FileObject_Num;i++)size += Memory_FileSize_Get((Memory_FileObject_EnumDef )i);
        }break;
    }
    return size;
}

/****************************************************
@function:读取文件起始地址
@param:object:对象,等于eMemory_FileObject_Num则表示读取未尾文件结束地址，也就是内存占用的结束地址
@return:起始地址
@note:
****************************************************/
uint32_t Memory_FileAddress_Get(Memory_FileObject_EnumDef object)
{
    uint32_t address = dMemory_FlashStartAddress;//起始地址

    if(object > eMemory_FileObject_Num)return 0;
    for(uint16_t i = 0;i < object;i++)
    {
        address += Memory_FileSize_Get((Memory_FileObject_EnumDef )i);
        address = (address/dMemory_FlashSmallestUnit + ((address%dMemory_FlashSmallestUnit)?1:0))*dMemory_FlashSmallestUnit;//占用整扇区
    }
    return address;
}

/****************************************************
@function:读取文件行数
@param:object:对象
@return:文件行数
@note:
****************************************************/
uint16_t Memory_FileLineNumber_Read(Memory_FileObject_EnumDef object)
{
    if(object >= eMemory_FileObject_Num)return 0;
    FileFormatLineNumber_StructDef line;
    W25QXXX_ReadData(Memory_FileAddress_Get(object),(uint8_t *)(&line),sizeof(line));
    if((line.identifyA != dVerifyIdentifyA) || (line.identifyB != dVerifyIdentifyB))return 0;
    return line.value;
}

/****************************************************
@function:写入文件行数
@param:object:对象
    number:文件行数
@return:void
@note:
****************************************************/
void Memory_FileLineNumber_Write(Memory_FileObject_EnumDef object,uint16_t number)
{
    if(object >= eMemory_FileObject_Num)return;
    FileFormatLineNumber_StructDef line;
    line.identifyA = dVerifyIdentifyA;
    line.identifyB = dVerifyIdentifyB;
    line.value = number;
    W25QXXX_WriteData(Memory_FileAddress_Get(object),(uint8_t *)(&line),sizeof(line));
}

/****************************************************
@function:读取文件内容
@param:object:对象
    index:文件行索引值,0开始
    content:文件内容
    number:行数
@return:假-读取失败;真-读取成功
@note:
****************************************************/
bool Memory_FileContent_Read(Memory_FileObject_EnumDef object,uint16_t index,void *content,uint16_t number)
{
    if((object >= eMemory_FileObject_Num) || (content == NULL) || (number == 0))return false;
    if(Memory_FileLineNumber_Read(object) == 0)return false;
    W25QXXX_ReadData((Memory_FileAddress_Get(object) + sizeof(FileFormatLineNumber_StructDef) + Memory_FileFormatContentSize_Get(object)*index),\
        (uint8_t *)content,\
        (Memory_FileFormatContentSize_Get(object) * number));
    return true;
}

/****************************************************
@function:写入文件内容
@param:object:对象
    index:文件行索引值,0开始
    content:文件内容
    number:行数
@return:void
@note:
****************************************************/
void Memory_FileContent_Write(Memory_FileObject_EnumDef object,uint16_t index,void *content,uint16_t number)
{
    if((object >= eMemory_FileObject_Num) || (content == NULL) || (number == 0))return;
    W25QXXX_WriteData((Memory_FileAddress_Get(object) + sizeof(FileFormatLineNumber_StructDef) + Memory_FileFormatContentSize_Get(object)*index),\
        (uint8_t *)content,\
        (Memory_FileFormatContentSize_Get(object) * number));
}

/****************************************************
@function:显示文件数据
@param:object:对象
@return:void
@note:
****************************************************/
void Memory_FileDataPrintf(Memory_FileObject_EnumDef object)
{
    uint16_t i = 0,num = 0;

    if(object >= eMemory_FileObject_Num)return;
    num = Memory_FileLineNumber_Read(object);
    dMemory_LOG("[%s]FileNumber=%d\n",dMemory_FileName(object),num);

    uint8_t buf[20] = {0};//应大于所有文件类型的行数据格式结构体其字节数
    for(i = 0;i < num;i++)
    {
        Memory_FileContent_Read(object,i,(void *)buf,1);
        switch(object)
        {
            case eMemory_FileObject_SystemConfiguration:
            {
                FileFormatContent_SystemConfiguration_StructDef *data = (FileFormatContent_SystemConfiguration_StructDef *)buf;
                dMemory_LOG("address:%d\n",\
                    data->address);
            }break;
            case eMemory_FileObject_User0:
            {
                if(i == 0)dMemory_LOG("SerialNumber temperature concentration VoltageTemperature[v] VoltageHydrogen[v]\n");
                FileFormatContent_User_StructDef *data = (FileFormatContent_User_StructDef *)buf;
                dMemory_LOG("%4d %8.3f %8.3f %8.3f %8.3f\n",\
                    data->SerialNumber,\
                    data->temperature,\
                    data->concentration,\
                    data->VoltageTemperature,\
                    data->VoltageHydrogen);
            }break;
            case eMemory_FileObject_VoltageTemperature:
            case eMemory_FileObject_VoltageHydrogen:
            {
                if(i == 0)dMemory_LOG("SerialNumber value real a b\n");
                FileFormatContent_CalibrationData_StructDef *data = (FileFormatContent_CalibrationData_StructDef *)buf;
                dMemory_LOG("%4d %8.3f %8.3f %8.3f %8.3f\n",\
                    data->SerialNumber,\
                    data->value,\
                    data->real,\
                    data->a,\
                    data->b);
            }break;
            default:return;
        }
    }
}
